<?php
/**
 * Database model for a piece of content
 *
 * These models represent all pieces of content of all content types, and handle
 * the modification, initialization, and the display of their data
 *
 * @package Concerto
 * @subpackage Models
 * @author Jason Raede <jason@torchdm.com>
 */
namespace application\models;
use application\utilities\Utility;
use application\system\Permalink;
use application\system\Gandalf;

class Content extends ContentBase {
	
	/**
	 * An array of feedback objects associated with the content
	 *
	 * @var array
	 */
	protected $feedbacks = array();
	
	/**
	 * The current feedback object in the loop
	 *
	 * @var \application\models\Feedback
	 */
	protected $currentFeedback;
	
	/**
	 * Initializes the content object based on the parameters
	 *
	 * If $params['id'] is set, the content is initialized with the information for the content
	 * with that ID. Otherwise if $params['type'] is set, the content is initialized with the
	 * knowledge of its type, and at least will be able to know which content fields it is
	 * supposed to have.
	 *
	 * @param string $params A query string with id or type
	 */
	public function __construct($params = null) {
		global $concerto;
		parse_str($params);
		if($id) {
			$contentType = DB()->getVar("SELECT `content_type` FROM `cn_content` WHERE `content_id`='$id'");
			$query = $concerto->ContentQuery->buildQuery(array('id'=>$id, 'type'=>$contentType, 'status'=>'ignore'));
			$row = DB()->getRow($query, 'array');
			//$this->data['content_id'] = $id;
			foreach($row as $column=>$value) {
				$this->dbFields[$column] = $value;
			}
			
			$this->data['content_type'] = $contentType;
		}
		elseif($type) {
			$this->data['content_type'] = $type;
			$contentType = $type;
		}
		else {
			$contentType = $this->dbFields['content_type'];
			$this->data['content_type'] = $contentType;
		}
		
		$fields =  DB()->getResults("SELECT * FROM `cn_content_fields` WHERE `cf_content_type`='$contentType' ORDER BY `cf_id`");
		foreach($fields as $field) {
			$this->fields[$field->cf_display_name] = $field;
			$column = ($field->cf_order_column) ? $field->cf_order_column : 1;
			if($field->cf_column != 'content_id')
				$this->fieldColumns[$column][] = $field;
			
			// Existing DB info --> clean data for displaying, form inputs
			$this->fromDB($field);
		}
		
		
		
	}
	
	
	
	/**
	 * Inserts or updates the row in the database
	 *
	 * The return array is the standard return type for all DBObject objects.
	 *
	 * @return array DBObject status array.
	 * @see \application\models\DBObject::save()
	 */
	public function save() {
		$this->getFormData();
		$this->toDB();
		$return = array();
		$contentType = $this->dbFields['cn_content']['content_type'];
		
		if(count($this->messages['error'])) {
			return array('status'=>'error', 'errors'=>$this->messages['error']);
		}
		else {
			if($content = $this->data['content_id']) { // Update
				DB()->update('cn_content', 'content_id', $content, $this->dbFields['cn_content']);
				DB()->update('cn_content_type'.$contentType, 'c'.$contentType.'_id', $content, $this->dbFields['cn_content_type']);
				
				if(count($this->dbFields['auxData'])) {
					foreach($this->fields as $field) {
						$class = 'application\\datatypes\\' . $field->cf_data_type;
						$class::endForm($field, $this->dbFields['auxData'], $content, $contentType);
					}
				}
				$this->messages['success'][] = 'Content updated successfully.';
				Modulator()->runFunction('content_saved', $this->dbFields);
			}
			else { // Add
				unset($this->dbFields['cn_content']['content_id']);
				DB()->insert('cn_content', $this->dbFields['cn_content']);
				$last = DB()->lastInsert();
				$content = $last;
				$this->dbFields['cn_content_type']['c'.$contentType.'_id'] = $last;
				$permalink = Permalink::buildContentPermalink($last);
				DB()->insert('cn_content_type'.$contentType, $this->dbFields['cn_content_type']);
				if(count($this->dbFields['auxData'])) {
					foreach($this->fields as $field) {
						$class = 'application\\datatypes\\' . $field->cf_data_type;
						$class::endForm($field, $this->dbFields['auxData'], $last, $contentType);
					}
				}
				$contentName = DB()->getVar("SELECT `ct_title` FROM `cn_content_types` WHERE `ct_id`='$contentType'");
				if($this->dbFields['cn_content']['content_status'] == 'pending') {
					if(loggedIn()) {
						$this->messages['success'][] = $contentName.' added successfully. It is currently pending review by our administrators; you will be notified when it has been approved. In the meantime, you can <a href="">add another '.$contentName.'.</a>';
					}
					else {
						$this->messages['success'][] = $contentName.' added successfully. It is currently pending review by our administrators. In the meantime, you can <a href="">add another '.$contentName.'.</a>';
					}
				}
				else {
					$this->messages['success'][] = $contentName.' added successfully. <a href="'.$permalink.'">View </a> or <a href="">add another '.$contentName.'.</a>';
				}
				$this->data = array();
				Modulator()->runFunction('content_added', $this->dbFields);
			}
			
			
			
		}
		
		return array('status'=>'success', 'id'=>$content);
	}
	
	
	/**
	 * Validates all data on this object with their respective DataType classes
	 * 
	 * @see \application\datatypes\DataType::validate()
	 */
	protected function toDB() {
		$results = array();
		// Use datatype-defined methods of validation
		foreach($this->fields as $field) {
			$class = 'application\\datatypes\\' . $field->cf_data_type;
			$results = $class::validate($field, $this->data, $results);
		}
		
		if(is_array($results['errors'])) {
			return $results;
		}
		else {
			
			$cn_content = array();
			$auxData = array();
			$cn_content_type = array();
			$contentType = $this->data['content_type'];
			foreach($results['db_fields'] as $field=>$value) {
				if(substr($field, 0, 7) == 'content') {
					$cn_content[$field] = $value;
				}
				elseif(substr($field, 0, 2) == 'c'.$contentType) {
					$cn_content_type[$field] = $value;
				}
				else {
					$auxData[$field] = $value;
				}
			}
			if($results['alerts']) $this->messages['alert'] = $results['alerts'];
			
			// Set the content type
			$cn_content['content_type'] = $contentType;
			$cn_content['content_id'] = $this->data['content_id'];
			
			$this->dbFields['cn_content'] = Utility::arrayFunction($cn_content, 'mysql_real_escape_string');
			$this->dbFields['cn_content_type'] = Utility::arrayFunction($cn_content_type, 'mysql_real_escape_string');
			$this->dbFields['auxData'] = Utility::arrayFunction($auxData, 'mysql_real_escape_string');
			
		}
	}
	
	

	/**
	 * Retrieves all feedback associated with this content
	 */
	protected function getFeedbacks() {
		$this->feedbacks = DB()->getResults("SELECT * FROM `cn_feedback` WHERE `feedback_content`='".$this->data['content_id']."' ORDER BY `feedback_timestamp` ASC", 'object', 'application\\models\\Feedback');
	}
	
	/**
	 * Gets the current feedback object in the loop
	 *
	 * @return \application\models\Feedback
	 */
	protected function getCurrentFeedback() {
		$current = current($this->feedbacks);
		return $current;
		
	}
	
	/**
	 * Initializes the current feedback and advances the internal array pointer
	 */
	public function initFeedback() {
		$this->currentFeedback = $this->getCurrentFeedback();
		next($this->feedbacks);
		
	}
	
	/**
	 * Checks if there are any more feedback objects that have not been looped over
	 *
	 * @return bool
	 */
	public function hasFeedback() {
		global $concerto;
		if(!count($this->feedbacks)) {
			$this->getFeedbacks();
		}
		$return = $this->getCurrentFeedback();
		return $return;
	}
	
	/**
	 * Returns the rating of the current feedback
	 *
	 * @return int
	 * @see \application\models\Feedback::feedbackRating()
	 */
	public function feedbackRating() {
		return $this->currentFeedback->feedbackRating();
	}
	
	/**
	 * Returns the comment of the current feedback
	 *
	 * @return string
	 * @see \application\models\Feedback::feedbackComment()
	 */
	public function feedbackComment() {
		return $this->currentFeedback->feedbackComment();
	}
	
	/**
	 * Returns the timestamp of the current feedback
	 *
	 * @param string The format with which to display the timestamp, using arguments for PHP's date() function
	 * @return string
	 * @see \application\models\Feedback::feedbackRating()
	 */
	public function feedbackTime($string = 'n/j/Y g:ia') {
		return $this->currentFeedback->feedbackTime($string);
	}
	
	/**
	 * Returns the user that posted the current feedback
	 *
	 * @return mixed If the user was logged in, this function returns an \application\models\User object. If not, it returns the display name the user gave when he posted the feedback
	 */
	public function feedbackUser() {
		return $this->currentFeedback->feedbackUser();
	}
	
	/**
	 * Deletes the content and all references in "linking tables" from the database
	 *
	 * @return array DBObject status array.
	 */
	public function delete() {
		DB()->query("DELETE FROM `cn_content` WHERE `content_id`='".$this->data['content_id']."'");
		DB()->query("DELETE FROM `cn_content_type".$this->data['content_type']."` WHERE `c".$this->data['content_type']."_id` = '".$this->data['content_id']."'");
		DB()->query("DELETE FROM `cn_content_tag_relations` WHERE `ctr_content`='".$this->data['content_id']."'");
		DB()->query("DELETE FROM `cn_content_content_relations` WHERE `ccr_content1`='".$this->data['content_id']."' OR `ccr_content2` = '".$this->data['content_id']."' OR `ccr_content3` = '".$this->data['content_id']."'");
		return array('status'=>'success');
	}
	
	
	/**
	 * Check if a content is a descendant of another content
	 *
	 * @param int $content The content to check
	 * @param int $ancestor The ancestor to check
	 * @return bool
	 */
	public static function isDescendantOfContent($content, $ancestor) {
		$descendants = self::getDescendants($ancestor, 999, TRUE);
		// Search results for $content
		
		foreach($descendants as $descendant) {
			if($descendant['id'] == $content) 
				return $descendant[$generation];
		}
		return FALSE;
		
	}
	
	/**
	 * Get all descendants of a content
	 *
	 * @param int $content
	 * @param int $generationsBack How many generations to go back?
	 * @param bool $returnGeneration Return an array with id and generation, or just the id?
	 * @param int $generation The current generation in the recursion
	 * @return array
	 */
	public static function getDescendants($content, $generationsBack, $returnGeneration = FALSE, $generation = 1) {
		global $concerto;
		
		$return = array();
		if($generationsBack) {
			$results = $concerto->DB->getResults("SELECT `content_id` FROM `cn_content` WHERE `content_parent`='$content'");
			if(count($results)) {
				foreach($results as $result) {
					if($returnGeneration) $return[] = array('id'=>$result->content_id, 'generation'=>$generation);
					else $return[] = $result->content_id;
					$return = array_merge($return, self::getDescendants($result->content_id, ($generationsBack - 1), TRUE, ($generation + 1)));
				}
				return $return;
			}
		}
		
		return array();
	}
	
	/**
	 * Get all content that has a tag
	 *
	 * @param int $tag
	 * @return array
	 */
	public static function getContentWithTag($tag) {
		$results = DB()->getVarArray("SELECT `content_id` FROM `cn_content` INNER JOIN `cn_content_tag_relations` ON `content_id`=`ctr_content` WHERE `ctr_tag`='$tag'");
		return $results;
	}
	
	
}

?>